// ==UserScript==
//
// @name         Last.fm Enhanced Song Page
// @description  Injects external song information to the last.fm track page
// @namespace    lastfmenhancedsongpage
//
// @include      http://www.last.fm/music/*/*/*
// @include      http://www.lastfm.*/music/*/*/*
//
// @exclude      /^http:\/\/www\.last\.?fm\.?\w*\/music\/.*\/\+(wiki|images|videos|news)\//
//
// @require      https://bitbucket.org/saaj/lastfm-enhanced-song-page/raw/default/build/base.js
// @require      https://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js
//
// @downloadURL  https://bitbucket.org/saaj/lastfm-enhanced-song-page/raw/default/build/98307.user.js
//
// @grant        GM_xmlhttpRequest
// @grant        GM_getResourceURL
//
// @resource     progress http://cdn.last.fm/tageditor/progress_active.gif
//
// @author       saaj <mail@saaj.me>
// @website      https://bitbucket.org/saaj/lastfm-enhanced-song-page
// @license      GPL-2.0+
// @version      0.7.6
//
// ==/UserScript==


/**
 * Formats self in modern Python manner
 */
String.prototype.format = function()
{
  var value = this;
  for(var i = 0; i < arguments.length; i++)
  {
    value = value.split('{' + i + '}').join(String(arguments[i]));
  }

  return value;
};
/**
 * Computes a Levenshtein distance of self and passed argument,
 * based on qx.util.EditDistance
 */
String.prototype.distance = function(value)
{
  var a = this;
  var b = value;
  
  // distance is table with a.length + 1 rows and b.length + 1 columns
  var distance = [];
  // posA and posB are used to iterate over a and b
  var posA, posB, cost;

  for(posA = 0; posA <= a.length; posA++)
  {
    distance[posA]    = [];
    distance[posA][0] = posA;
  }

  for(posB = 1; posB <= b.length; posB++) 
  {
    distance[0][posB] = posB;
  }

  for(posA = 1; posA <= a.length; posA++)
  {
    for(posB = 1; posB <= b.length; posB++)
    {
      cost = a[posA - 1] === b[posB - 1] ? 0 : 1;

      distance[posA][posB] = Math.min(
        distance[posA - 1][posB    ] + 1,     // deletion
        distance[posA    ][posB - 1] + 1,     // insertion
        distance[posA - 1][posB - 1] + cost   // substitution
      );
    }
  }

  return distance[posA - 1][posB - 1];
};

function namespace(ns)
{
  var root = window;
  ns.split(".").forEach(function(part)
  {
    if(typeof root[part] == "undefined")
    {
      root[part] = {};
    }

    root = root[part];
  });
  return root;
}


namespace("lesp");

/**
 * Event target. Sort of conceptual mix of qx.core.Object and SplObserver/SplSubject.
 */
lesp.Target = Base.extend({

  _listeners : null,


  constructor : function(supportedEvents)
  {
    this._listeners = {};
    supportedEvents.forEach(function(name)
    {
      this._listeners[name] = [];
    }, this);
  },

  attach : function(event, listener)
  {
    this._listeners[event].push(listener);
  },

  detach : function(event, listener)
  {
    var i = this._listeners[event].indexOf(listener);
    if(i != -1)
    {
      this._listeners[event].splice(i, 1);
    }
  },

  notify : function(event, data)
  {
    this._listeners[event].forEach(function(listener)
    {
      listener(this, data);
    }, this);
  }

});

lesp.mangleHtml = function(value)
{
  // the trick allows jquery traverse html correctly
  return value.replace(/<(\/?)(html|head|body|img)/ig, '<$1$2z');
};


